package cn.apthink.schedule;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import cn.apthink.calendar.Department;
import cn.apthink.calendar.DepartmentCalendarHelper;
import cn.apthink.calendar.DepartmentHelper;
import cn.apthink.calendar.DepartmentWorker;
import cn.apthink.calendar.Holiday;
import cn.apthink.calendar.SpecialWorkDay;
import cn.apthink.calendar.WeekTemplateDetail;
import cn.apthink.calendar.WorkCalendar;
import cn.apthink.utility.DateUtil;
import cn.apthink.utility.ObjectUtil;
import cn.apthink.utility.Util;
import lombok.Data;

@Data
public class ScheduleHelper {
	private EfficiencyHelper efficiencyHelper;
	private DepartmentCalendarHelper departmentCalendarHelper;
	private EvaluationHelper evaluationHelper;

	private List<Line> lines;
	private LocalDate startDate;
	private LocalDate endDate;
//	private LocalDate scheduleStartDate;

	// 优化总时长
	private long duration = 60;
	// 排产任务执行时长
	private int taskRunMillSeconds = 500;
	// 排产任务队列
	private List<ScheduleTask> tasks = new ArrayList<ScheduleTask>();
	// 循环迭代的次数
	public long iterationCount = 0;
	// 方案队列长度
	private int soluationSize = 10;

	public void init(int taskCount) {
		departmentCalendarHelper.setRange(startDate, endDate);
		for (Line line : lines) {
			line.setCalendar(departmentCalendarHelper.getDepartmentCalendar(line.getId()));
			line.setEfficiencyHelper(efficiencyHelper);
			line.refresh(startDate.atStartOfDay());
		}
		int i = taskCount;
		tasks.clear();
		while (i > 0) {
			ScheduleTask task = new ScheduleTask("ScheduleTask_" + i, soluationSize, taskRunMillSeconds);
//			task.setStartDate(scheduleStartDate);
			// David: 克隆所有产线，避免多线程处理相互干扰
			List<Line> ls = new ArrayList<Line>();
			lines.forEach(x -> {
				Line l = ObjectUtil.clone(x);
				l.setPlans(ObjectUtil.cloneList(x.getPlans()));
				ls.add(l);
			});
			task.setLines(ls);
			task.setEvaluationHelper(evaluationHelper);
			tasks.add(task);
			i--;
		}
	}

	// David: 仅提供此初始化类
	public ScheduleHelper(LocalDate startDate, LocalDate endDate, DepartmentCalendarHelper departmentCalendarHelper,
			EfficiencyHelper efficiencyHelper, EvaluationHelper evaluationHelper, List<Line> lines) {
		this.startDate = startDate;
		this.endDate = endDate;
		this.departmentCalendarHelper = departmentCalendarHelper;
		this.efficiencyHelper = efficiencyHelper;
		this.evaluationHelper = evaluationHelper;
		this.lines = lines;
	}

	public void refreshLine(LocalDate startDate, boolean forceCompact) {
		for (Line line : lines) {
			line.refresh(startDate.atStartOfDay(), forceCompact);
		}
	}

	//David: 按指定方案刷新排产计划的所有信息（务必不要克隆列表中计划对象）
	public void refreshSoluationPlan(List<Plan> plans, Soluation soluation) {
		List<Plan> ps = new ArrayList<Plan>();
		List<PlanKPI> pks = soluation.getPlanKPIs();
		for (int i = 0, l = pks.size(); i < l; i++) {
			PlanKPI pk = pks.get(i);
			Plan p = plans.stream().filter(x -> {
				return x.getId().equals(pk.getPlanId());
			}).findFirst().get();
			p.setDepartmentId(pk.getDepartmentId());
			ps.add(p);
		}
		tasks.get(0).removeUnlockPlans();
		tasks.get(0).setPlans(ps);
		tasks.get(0).getInitSoluation();
	}

	public void removeUnlockPlans() {
		for (Line line : lines) {
			line.removeUnlockPlans();
		}
	}

	public List<Plan> getUnlockPlans(LocalDate startDate) {
		List<Plan> unlockPlans = new ArrayList<Plan>();
		for (Line line : lines) {
			List<Plan> datas = line.getUnlockPlans(startDate);
			if (!Util.isEmpty(datas))
				unlockPlans.addAll(datas);
		}
		return unlockPlans;
	}
	
	private void initTasks(List<Plan> plans) {
		for (ScheduleTask task: tasks) {
			List<Plan> ps = new ArrayList<Plan>();
			for (int i=0,l=plans.size();i<l;i++) {
				Plan p = ObjectUtil.clone(plans.get(i));
				p.setDetails(ObjectUtil.cloneList(plans.get(i).getDetails()));
				ps.add(p);
			}
			task.setPlans(ps);
		}
	}
	
	public Soluation getInitSoluation(List<Plan> plans) {
		// David: 初始化原始方案（务必不要多线程处理）
		sort2(plans);
		tasks.get(0).setPlans(plans);
		return tasks.get(0).getInitSoluation();		
	}

	public SoluationHelper autoSchedule(SoluationHelper soluationHelper, List<Plan> plans) {
		iterationCount = 0;
		// David: 先优化一次获取更多方案
		sort(plans);
		initTasks(plans);
		ScheduleTask task1 = tasks.get(0);
		Thread t = new Thread(task1);
		try {
			t.start();
			t.join();
			soluationHelper.add(task1.getSoluations());
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		long s = System.currentTimeMillis() + 1000 * duration;
		List<Soluation> ss;
		List<String> ids;
		int index;
		int taskSize = tasks.size() == 1 ? 1 : tasks.size() / 2;
		while (true) {
			// 迭代已有最优方案
			for (int i = 0, l = taskSize; i < l; i++) {
				ss = soluationHelper.topSoluations();
				index = Util.randInt(0, ss.size());
				ids = ss.get(index).getIds();
				tasks.get(i).changePlans(ids);
			}
			// 迭代已有方案
			for (int i = taskSize, l = tasks.size(); i < l; i++) {
				ss = soluationHelper.getSoluations();
				index = Util.randInt(0, ss.size());
				ids = ss.get(index).getIds();
				tasks.get(i).changePlans(ids);
			}

			try {
				List<Thread> ts = new ArrayList<Thread>();
				for (int i = 0, l = tasks.size(); i < l; i++) {
					ts.add(new Thread(tasks.get(i)));
				}
				for (int i = 0, l = ts.size(); i < l; i++) {
					ts.get(i).start();
				}
				for (int i = 0, l = ts.size(); i < l; i++) {
					ts.get(i).join();
				}
				for (int i = 0, l = tasks.size(); i < l; i++) {
					soluationHelper.add(tasks.get(i).getSoluations());
				}
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}

			iterationCount++;
			if (s < System.currentTimeMillis())
				break;
		}
		return soluationHelper;
	}

	public static void sort(List<Plan> plans) {
		plans.sort((a, b) -> {
			if (!Util.equals(a.earliestStartDate(), b.earliestStartDate()))
				return Util.compareTo(a.earliestStartDate(), b.earliestStartDate());
			if (!Util.equals(a.latestEndDate(), b.latestEndDate()))
				return Util.compareTo(a.latestEndDate(), b.latestEndDate());
			return Util.compareTo(b.planAmount(), a.planAmount());
		});
	}

	public static void sort2(List<Plan> plans) {
		plans.sort((a, b) -> {
			// 优先已排的计划
			if (!Util.equals(a.getStartTime(), b.getStartTime()))
				return Util.isEmpty(a.getStartTime()) ? 1 : Util.compareTo(a.getStartTime(), b.getStartTime());
			if (!Util.equals(a.getDepartmentId(), b.getDepartmentId()))
				return Util.isEmpty(a.getDepartmentId()) ? 1 : Util.compareTo(a.getDepartmentId(), b.getDepartmentId());
			if (!a.earliestStartDate().equals(b.earliestStartDate()))
				return Util.compareTo(a.earliestStartDate(), b.earliestStartDate());
			if (!a.latestEndDate().equals(b.latestEndDate()))
				return Util.compareTo(a.latestEndDate(), b.latestEndDate());
			return Util.compareTo(b.planAmount(), a.planAmount());
		});
	}

	public static ScheduleHelper testData() {
		List<Department> lstDepartment = new ArrayList<Department>();
		lstDepartment.add(new Department("factory", null, 8));
		lstDepartment.add(new Department("line01", "factory", 2));
		lstDepartment.add(new Department("line02", "factory", 2));
		lstDepartment.add(new Department("line03", "factory", 2));
		lstDepartment.add(new Department("line04", "factory", 2));
		lstDepartment.add(new Department("line05", "factory", 2));
		lstDepartment.add(new Department("line06", "factory", 2));
		lstDepartment.add(new Department("line07", "factory", 2));
		lstDepartment.add(new Department("line08", "factory", 2));
		lstDepartment.add(new Department("line09", "factory", 2));
		lstDepartment.add(new Department("line10", "factory", 2));

		List<Product> lstProduct = Product.testDatas();
		List<Efficiency> lstEfficiency = new ArrayList<Efficiency>();
		lstEfficiency.add(new Efficiency("line01", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line02", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line03", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line04", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line05", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line06", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line07", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line08", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line09", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("line10", "p1", 0, 0.7));
		lstEfficiency.add(new Efficiency("factory", "p", 0, 0.7));

		List<WorkCalendar> lstWorkCalendar = new ArrayList<WorkCalendar>();
		lstWorkCalendar.add(new WorkCalendar("factory", WorkCalendar.WeekTemplateDefault, "2024-01-01"));

		ProductHelper productHelper = new ProductHelper(lstProduct);
		DepartmentHelper departmentHelper = new DepartmentHelper(lstDepartment);
		EfficiencyHelper efficiencyHelper = new EfficiencyHelper(departmentHelper, productHelper, lstEfficiency);

		DepartmentCalendarHelper departmentCalendarHelper = new DepartmentCalendarHelper(lstDepartment, null,
				WeekTemplateDetail.testData(), lstWorkCalendar, null, null);

		Evaluation evaluation = Evaluation.testData();
		EvaluationHelper evaluationHelper = new EvaluationHelper();
		evaluationHelper.setEvaluation(evaluation);

		List<Line> lines = new ArrayList<Line>();
		Line line = new Line("line01", 2);
		line.setValidStartDate(LocalDate.parse("2025-01-10"));
		lines.add(line);
		line = new Line("line02", 2);
		line.setValidStartDate(LocalDate.parse("2025-01-10"));
		lines.add(line);
		line = new Line("line03", 2);
		line.setValidStartDate(LocalDate.parse("2025-01-10"));
		lines.add(line);
		line = new Line("line04", 2);
		line.setValidStartDate(LocalDate.parse("2025-01-10"));
		lines.add(line);
		line = new Line("line05", 2);
		line.setValidStartDate(LocalDate.parse("2025-01-10"));
		lines.add(line);

		ScheduleHelper helper = new ScheduleHelper(LocalDate.parse("2024-10-01"), LocalDate.parse("2025-10-01"),
				departmentCalendarHelper, efficiencyHelper, evaluationHelper, lines);
//		helper.setEvaluationHelper(evaluationHelper);
//		helper.setScheduleStartDate(LocalDate.parse("2025-01-10"));
//		helper.setLines(lines);
		// David：在加入ScheduleHelper后初始化Line基础数据后，才能进行刷新操作
//		lines.forEach(x -> {
//			x.refresh(DateUtil.toDateTime("2025-01-01 00:00:00"));
//		});
		return helper;
	}

}
